#pragma once

#include <occmesh/api/meshdef.h>

#include <map>
#include <stack>

/**
 * @file Feature.h
 * @author xingyifan
 * @date 2023-08-14 17:28
 *
 * @description: 特征类，保存边的特征值
 */

class Feature
{
  private:
    Mesh::EdgeHandle m_edge; // 记录边
    float m_value;           // 特征值

  public:
    Feature() : m_edge(), m_value(0)
    {
    }
    Feature(const Mesh::EdgeHandle &edge, float value) : m_edge(edge), m_value(value)
    {
    }

    float value() const
    {
        return m_value;
    }
    const Mesh::EdgeHandle &edge() const
    {
        return m_edge;
    }
    bool operator==(const Feature &other) const
    {
        return edge() == other.edge();
    }

    // 特征比较器
    static bool greater(const Feature &a, const Feature &b)
    {
        return a.value() > b.value();
    }
};

/**
 * @file Feature.h
 * @author xingyifan
 * @date 2023-08-14 17:38
 *
 * @description: 保存特征半边的集合
 */

class FeatureSet
{
  private:
    static Mesh::Scalar s_count;

  private:
    Mesh::Scalar m_id;         // 标记特征集
    HalfedgeList m_halfedges;  // 记录特征半边
    Mesh::Scalar m_totalValue; // 记录集合中特征值的和

  public:
    FeatureSet() : m_id(s_count++), m_totalValue(0)
    {
    }

    Mesh::Scalar id() const
    {
        return m_id;
    }
    size_t size() const
    {
        return m_halfedges.size();
    }
    bool empty() const
    {
        return m_halfedges.empty();
    }
    HalfedgeList &halfedges()
    {
        return m_halfedges;
    }
    const HalfedgeList &halfedges() const
    {
        return m_halfedges;
    }

    void clear();
    bool add(const Mesh::HalfedgeHandle &halfedge, Mesh::Scalar value); // 加入新的特征半边
    bool contains(const Mesh::HalfedgeHandle &halfedge) const;          // 检查是否包含该特征半边
};

/**
 * @file Feature.h
 * @author xingyifan
 * @date 2023-08-14 18:00
 *
 * @description: 特征度量虚类
 */

class FeatureMetric
{
  public:
    virtual float eval(const Mesh *mesh, const Mesh::EdgeHandle &edge) = 0;
    virtual bool operator>(float value) = 0;

    virtual float threshold() const = 0;
};

// Second Order Differences (SOD) - Angle between the normals
class SODFeatureMetric : public FeatureMetric
{
  private:
    float m_threshold; // 特征阈值

  public:
    SODFeatureMetric(float threshold) : m_threshold(threshold)
    {
    }

    virtual float eval(const Mesh *mesh, const Mesh::EdgeHandle &edge)
    {
        // 利用二面角估计 SOD 二阶差分（法向之间的夹角），弧度转换为角度
        return OpenMesh::rad_to_deg(std::fabs(mesh->calc_dihedral_angle_fast(edge)));
    }

    virtual bool operator>(float value)
    {
        return m_threshold > value;
    }
    virtual float threshold() const
    {
        return m_threshold;
    }
};

/**
 * @file FeatureBuilder.h
 * @author xingyifan
 * @date 2023-08-17 14:41
 *
 * @description: 特征构建器
 */

class FeatureBuilder
{
  private:
    // 搜索状态，用来构建半边句柄的链表
    struct SearchStatus
    {
        Mesh::HalfedgeHandle parent; // 前一个半边的句柄
        Mesh::Normal dir;            // 半边的方向
        float valueTotal;            // 记录从链表开头到目前为止的特征和
        int depth;                   // 在链表中的深度
        bool included;               // 是否已经包含在路径中

        SearchStatus() : parent(), dir(), valueTotal(0), depth(0), included(false)
        {
        }
        SearchStatus(const Mesh::Normal &d, float v) : parent(), dir(d), valueTotal(v), depth(0), included(false)
        {
        }
    };

    // 网格状态
    enum class MeshStatus
    {
        MS_None,     // 无
        MS_Feature,  // 特征
        MS_Neighbor, // 特征邻域
    };

    // 特征集的集合
    std::vector<FeatureSet> m_featureSets;

    // 记录网格和特征度量
    Mesh *m_mesh;
    FeatureMetric *m_metric;

    // 边和半边标量句柄，用于增加属性值
    OpenMesh::EPropHandleT<Mesh::Scalar> m_featureValue;
    OpenMesh::HPropHandleT<MeshStatus> m_meshStatus;

    // 记录两个关键变量：最大规模和最小长度
    const int m_maxStringSize;
    const int m_minFeatureLength;

  public:
    FeatureBuilder(Mesh *mesh, FeatureMetric *metric, int maxSetSize = 5, int minFeatureLength = 15);
    ~FeatureBuilder();

    // 标准构建函数
    const std::vector<FeatureSet> &Build();

  private:
    void expand(const Mesh::HalfedgeHandle &halfedge, FeatureSet &featureSet);
    void tagHalfEdge(const Mesh::HalfedgeHandle &feature);
    void tagNeighborhood(const Mesh::VertexHandle &vertex);
    double depthFirst(const Mesh::HalfedgeHandle &baseEdge, HalfedgeList &path, FeatureSet &featureSet);
};